package com.bianguo.android.iocdemo.view.widget;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.PointFEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;

import com.bianguo.android.iocdemo.R;

public class DragBubbleView extends View {

    public static final int BUBBLE_STATE_DEFAULT = 0;
    public static final int BUBBLE_STATE_COLLECT = 1;
    public static final int BUBBLE_STATE_DISMISS = 2;
    public static final int BUBBLE_STATE_BLAST = 3;
    private int mBubState;
    private Paint mBubPaint, mTxtPaint;
    private Path mBezierPath;
    private PointF mBubFixedPoint, mBubMovedPoint;
    private float mBubRadius, mBubFixedRadius, mBubMovedRadius;
    private Rect mTxtRect, mBurstRect;
    private float moveOffset;
    private String mTxtStr;
    private float mDist;
    private float maxDist;
    private Bitmap[] mBurstBitmapArray;
    private int[] mBurstDrawableArray;
    private int mCurrentBitmapIndex;
    private OnMoveListener onMoveListener;

    public DragBubbleView(Context context) {
        this(context, null);
    }

    public DragBubbleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragBubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DragBubbleView);
        String text = typedArray.getString(R.styleable.DragBubbleView_text);
        typedArray.recycle();
        mBubRadius = 28;
        mBubFixedRadius = mBubRadius;
        mBubMovedRadius = mBubFixedRadius;

        maxDist = mBubRadius * 8;
        moveOffset = maxDist / 4;
        mBubPaint = new Paint();
        mBubPaint.setStyle(Paint.Style.FILL);
        mBubPaint.setAntiAlias(true);
        mBubPaint.setColor(Color.RED);

        mTxtPaint = new Paint();
        mTxtPaint.setColor(Color.WHITE);
        mTxtPaint.setTextSize(32);
        mTxtPaint.setAntiAlias(true);

        mBezierPath = new Path();
        mTxtRect = new Rect();
        mBurstRect = new Rect();

        mBubState = BUBBLE_STATE_DEFAULT;
        mTxtStr = "5";

        if (!TextUtils.isEmpty(text)) {
            mTxtStr = text;
        }

        mBurstDrawableArray = new int[]{R.drawable.explosion_one, R.drawable.explosion_two, R.drawable.explosion_three,
        R.drawable.explosion_four, R.drawable.explosion_four};
        mBurstBitmapArray = new Bitmap[mBurstDrawableArray.length];
        for (int i = 0; i < mBurstDrawableArray.length; i++) {
            Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), mBurstDrawableArray[i]);
            mBurstBitmapArray[i] = bitmap;
        }
    }

    public void setText(String text) {
        mTxtStr = text;
    }

    public String getText() {
        return mTxtStr;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(getMeasureSize(widthMeasureSpec), getMeasureSize(heightMeasureSpec));
    }

    private int getMeasureSize(int measureSpec) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        if (MeasureSpec.EXACTLY == mode) {
            result = size;
        } else {
            result = (int) (2 * mBubRadius);
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (mBubFixedPoint == null) {
            mBubFixedPoint = new PointF();
        }
        mBubFixedPoint.set(w / 2, h / 2);

        if (mBubMovedPoint == null) {
            mBubMovedPoint = new PointF();
        }
        mBubMovedPoint.set(w / 2, h / 2);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBubState != BUBBLE_STATE_BLAST) {
            canvas.drawCircle(mBubMovedPoint.x, mBubMovedPoint.y, mBubMovedRadius, mBubPaint);
            mTxtPaint.getTextBounds(mTxtStr, 0, mTxtStr.length(), mTxtRect);
            canvas.drawText(mTxtStr, mBubMovedPoint.x - mTxtRect.width() / 2, mBubMovedPoint.y + mTxtRect.height() / 2, mTxtPaint);
        }
        if (mBubState == BUBBLE_STATE_COLLECT) {
            canvas.drawCircle(mBubFixedPoint.x, mBubFixedPoint.y, mBubFixedRadius, mBubPaint);
            float controlX = (mBubFixedPoint.x + mBubMovedPoint.x) / 2;
            float controlY = (mBubFixedPoint.y + mBubMovedPoint.y) / 2;

            float sin = (mBubMovedPoint.y - mBubFixedPoint.y) / mDist;
            float cos = (mBubMovedPoint.x - mBubFixedPoint.x) / mDist;

            float APointX = mBubMovedPoint.x - mBubMovedRadius * sin;
            float APointY = mBubMovedPoint.y + mBubMovedRadius * cos;

            float DPointX = mBubMovedPoint.x + mBubMovedRadius * sin;
            float DPointY = mBubMovedPoint.y - mBubMovedRadius * cos;

            float BPointX = mBubFixedPoint.x  - mBubFixedRadius * sin;
            float BPointY = mBubFixedPoint.y  + mBubFixedRadius * cos;

            float CPointX = mBubFixedPoint.x + mBubFixedRadius * sin;
            float CPointY = mBubFixedPoint.y - mBubFixedRadius * cos;

            mBezierPath.reset();
            mBezierPath.moveTo(APointX, APointY);
            mBezierPath.quadTo(controlX, controlY, BPointX, BPointY);
            mBezierPath.lineTo(CPointX, CPointY);
            mBezierPath.quadTo(controlX, controlY, DPointX, DPointY);
            mBezierPath.close();
            canvas.drawPath(mBezierPath, mBubPaint);
        }
        if (BUBBLE_STATE_BLAST == mBubState && mCurrentBitmapIndex < mBurstBitmapArray.length) {
            mBurstRect.set(
                    (int)(mBubMovedPoint.x - mBubMovedRadius),
                    (int)(mBubMovedPoint.y - mBubMovedRadius),
                    (int)(mBubMovedPoint.x + mBubMovedRadius),
                    (int)(mBubMovedPoint.y + mBubMovedRadius)
            );
            canvas.drawBitmap(mBurstBitmapArray[mCurrentBitmapIndex], null, mBurstRect, mBubPaint);
        }
        //1.静止状态，一个小球和消息数

        //2.连接状态，一个小球和消息数，两条贝塞尔曲线，一个原本位置的小球，半径可变

        //3.断开连接，一个小球和消息数

        //4.爆炸状态
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mBubState != BUBBLE_STATE_BLAST) {
                    if (onMoveListener != null) {
                        onMoveListener.onTouch(event.getX(), event.getY());
                    }
                    getParent().requestDisallowInterceptTouchEvent(true);
                    mDist = (float) Math.hypot(event.getX() - mBubFixedPoint.x, event.getY() - mBubFixedPoint.y);
                    if (mDist < mBubRadius + moveOffset) {
                        mBubState = BUBBLE_STATE_COLLECT;
                    } else {
                        mBubState = BUBBLE_STATE_DEFAULT;
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (BUBBLE_STATE_DEFAULT != mBubState) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    float preX = event.getX();
                    float preY = event.getY();
                    mDist = (float) Math.hypot(preX - mBubFixedPoint.x, preY - mBubFixedPoint.y);
                    mBubMovedPoint.x =  preX;
                    mBubMovedPoint.y =  preY;
                    if (BUBBLE_STATE_COLLECT == mBubState) {
                        if (mDist < maxDist - moveOffset) {
                            mBubFixedRadius = mBubRadius - mDist / 8;
                        } else {
                            mBubState = BUBBLE_STATE_DISMISS;
                        }
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                getParent().requestDisallowInterceptTouchEvent(false);
                if (BUBBLE_STATE_COLLECT == mBubState) {
                    startBubResetAnim();
                } else if (BUBBLE_STATE_DISMISS == mBubState) {
                    if (mDist < 2 * mBubRadius) {
                        startBubResetAnim();
                    } else {
                        startBubBustAnim();
                    }
                }
                break;
        }
        return true;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void startBubResetAnim() {
        ValueAnimator animator = ValueAnimator.ofObject(new PointFEvaluator(),
                new PointF(mBubMovedPoint.x, mBubMovedPoint.y),
                new PointF(mBubFixedPoint.x, mBubFixedPoint.y));
        animator.setInterpolator(new OvershootInterpolator(5f));
        animator.setDuration(200);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mBubMovedPoint = (PointF) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                mBubState = BUBBLE_STATE_DEFAULT;
            }
        });
        animator.start();
    }

    private void startBubBustAnim() {
        mBubState = BUBBLE_STATE_BLAST;
        ValueAnimator animator = ValueAnimator.ofInt(0, mBurstBitmapArray.length);
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(500);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mCurrentBitmapIndex = (int) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        animator.start();
    }

    public void setOnMoveListener(OnMoveListener onMoveListener) {
        this.onMoveListener = onMoveListener;
    }

    public interface OnMoveListener {

        void onTouch(float x, float y);

        void onActionUp();
    }
}
